package ontoTree.ontoTree;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntProperty;
import com.hp.hpl.jena.ontology.OntResource;
import com.hp.hpl.jena.ontology.Ontology;
import com.hp.hpl.jena.ontology.Restriction;
import com.hp.hpl.jena.ontology.UnionClass;
import com.hp.hpl.jena.ontology.impl.IntersectionClassImpl;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
import com.hp.hpl.jena.vocabulary.OWL;

/**
 * Cria a JTree que mostra a hierarquia de classes da ontologia
 * 
 * @author ligia
 * 
 */
public class OntologyTree {

	protected OntModel model;

	protected Set unusedConcepts;

	protected JTree ontologyTree;

	protected final String thing = OWL.Thing.getURI();

	protected final String nothing = OWL.Nothing.getURI();
	protected Ontology ont;
	protected String type;
	DefaultMutableTreeNode owlTreeThing = null;;
	DefaultMutableTreeNode raizDta = null;
	DefaultMutableTreeNode raizObj = null;
	boolean first1 = true;
	boolean first2 = true;

	public OntologyTree(OntModel m) {
		this.model = m;

	}

	/**
	 * Retorna a hierarquia de classes da ontologia
	 * 
	 * @return
	 * 
	 */
	public JTree getOntologyTree() {

		// ont = (Ontology) model.listOntologies().next();
		this.type = type;

		// JOptionPane.showMessageDialog(null, ont.getURI());

		// Obt�m as URI�s da classe Thing e Nothing
		OntClass owlThing = model.getOntClass(thing);
		OntClass owlNothing = model.getOntClass(nothing);

		/*
		 * Obt�m os conceitos que n�o devem ser usados, tais como a classe
		 * owl:Nothing
		 */
		unusedConcepts = collect(owlNothing.listEquivalentClasses());

		// Monta a arvore tendo como raiz a classe owl:Thing
		owlTreeThing = createTree(owlThing);
		createTree2(owlThing);

		// Cria a arvore
		ontologyTree = new JTree(new DefaultTreeModel(owlTreeThing));
		// Adiciona o Listener para capturar o n� selecionado
		// ontologyTree.addTreeSelectionListener(this);

		// Adiciona o render para personalizar os �cones
		ontologyTree.setCellRenderer(new OntologyTreeRenderer());

		return ontologyTree;

	}

	/*
	 * M�todo auxiliar para montar um conjunto com os conceitos que n�o
	 * devem ser considerados
	 */

	public Set collect(Iterator i) {
		Set set = new HashSet();
		while (i.hasNext()) {
			OntResource res = (OntResource) i.next();
			if (res.isAnon())
				continue;

			set.add(res);
		}

		return set;
	}

	/**
	 * Cria o n� raiz para um determinado conceito(classe) e adiciona como
	 * filhos dessa classe suas propriedades e subclasses
	 * 
	 * @param concepts
	 * @return O n�. Nulo, para owl:Nothing
	 */
	DefaultMutableTreeNode createTree(OntClass cls) {
		if (unusedConcepts.contains(cls))
			return null;

		DefaultMutableTreeNode root = createNode(cls);

		Set processedSubcls = new HashSet();

		// obt�m somente as subclasses diretas da classe
		Iterator subs = cls.listSubClasses(true);

		while (subs.hasNext()) {
			OntClass sub = (OntClass) subs.next();

			if (sub.isAnon())
				continue;

			if (processedSubcls.contains(sub))
				continue;

			DefaultMutableTreeNode node = createTree(sub);
			// ser� nulo se for owl:Nothing
			if (node != null) {

				root.add(node);

				processedSubcls.add(node.getUserObject());

			}
		}

		return root;
	}

	/**
	 * ------------------------------------------------------------------------
	 * -----------
	 **/
	void createTree2(OntClass cls) {
		if (unusedConcepts.contains(cls))
			return;

		addProperties(cls);

		Set processedSubcls = new HashSet();

		// obt�m somente as subclasses diretas da classe
		Iterator subs = cls.listSubClasses(true);

		while (subs.hasNext()) {
			OntClass sub = (OntClass) subs.next();

			if (sub.isAnon())
				continue;

			if (processedSubcls.contains(sub))
				continue;

			createTree2(sub);

			processedSubcls.add(sub);

		}

	}

	/**
	 * ------------------------------------------------------------------------
	 * -------------
	 **/

	/**
	 * Cria um TreeNode para uma determinada classe
	 * 
	 * @param entidade
	 *            (classe)
	 * @return
	 */
	DefaultMutableTreeNode createNode(OntResource cls) {
		return new DefaultMutableTreeNode(cls);
	}

	/**
	 * Adiciona as propriedades vinculadas a classe na Jtree
	 * 
	 * @param clsNode
	 * @return
	 * 
	 */
	void addProperties(OntClass c) {

		if (c.getURI().equals(thing) || c.getURI().equals(nothing))
			return;
		OntProperty p;
		DefaultMutableTreeNode n;

		// Adiciona as datatype properties
		for (Iterator i = model.listDatatypeProperties(); i.hasNext();) {
			p = (OntProperty) i.next();
			if (isDomain(c, p)) {
				if (first1) {
					raizDta = new DefaultMutableTreeNode("Datatype Properties");
					owlTreeThing.add(raizDta);
					first1 = false;

				}
				n = new DefaultMutableTreeNode(p);
				raizDta.add(n);

			}

		}

		// Adiciona as object properties
		for (Iterator i = model.listObjectProperties(); i.hasNext();) {
			p = (OntProperty) i.next();
			if (isDomain(c, p)) {
				if (first2) {
					raizObj = new DefaultMutableTreeNode("Object Properties");
					owlTreeThing.add(raizObj);
					first2 = false;

				}
				n = new DefaultMutableTreeNode(p);
				raizObj.add(n);

			}

		}
		// restProps.clear();
		// restProps = processRestriction(c);
		// for (Iterator i = restProps.keySet().iterator(); i.hasNext();) {
		// p = (OntProperty) restProps.get(i.next());
		// n = new DefaultMutableTreeNode(p);
		// clsNode.add(n);
		// AuxMethods.nodes.put(p, n); // TODO melhorar
		// }

		// return clsNode;
	}

	boolean isDomain(OntClass c, OntProperty p) {

		OntResource r = p.getDomain();
		if (r != null) {
			if (r.canAs(UnionClass.class)) {
				UnionClass uc = (UnionClass) r.as(UnionClass.class);
				ExtendedIterator domainIt = uc.listOperands();
				while (domainIt.hasNext()) {
					if (((OntClass) domainIt.next()).getURI()
							.equals(c.getURI()))

						return true;

				}

			}

			else if (r.getURI().equals(c.getURI()))
				return true;
		}
		return false;

	}

	HashMap<String, OntProperty> processRestriction(OntClass c) {

		OntClass t = null;
		IntersectionClassImpl clsAux = null;
		HashMap<String, OntProperty> restProps = new HashMap<String, OntProperty>();
		restProps.clear();
		for (Iterator i = c.listSuperClasses(); i.hasNext();) {
			OntClass supCls = (OntClass) i.next();

			if (supCls.isRestriction()) {
				Restriction r = supCls.asRestriction();

				if (!restProps.containsKey(r.getOnProperty().getURI()))
					restProps
							.put(r.getOnProperty().getURI(), r.getOnProperty());

				// if (r.isSomeValuesFromRestriction()) {
				// SomeValuesFromRestriction av = r
				// .asSomeValuesFromRestriction();

				// System.out.println("AllValuesFrom class " +
				// * av.getSomeValuesFrom().getURI() + " on property " +
				// * av.getOnProperty().getURI()); }
				// */
			}
		}
		if (c.getLocalName().equals("Student")) {
			for (Iterator i = c.listEquivalentClasses(); i.hasNext();) {
				t = (OntClass) i.next();
			}

			if (t.isIntersectionClass())
				clsAux = (IntersectionClassImpl) t.asIntersectionClass();
			else if (c.getEquivalentClass() != null
					&& c.getEquivalentClass().isIntersectionClass())
				clsAux = null;
			if (clsAux != null) {
				for (Iterator i = clsAux.listOperands(); i.hasNext();) {
					OntClass clsRest = (OntClass) i.next();
					if (clsRest.isRestriction()) {
						Restriction r = clsRest.asRestriction();
						if (!restProps.containsKey(r.getOnProperty().getURI()))
							restProps.put(r.getOnProperty().getURI(), r
									.getOnProperty());
					}
				}
			}
		}
		return restProps;

	}

}